package org.cyhz.standardize.service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.cyhz.domain.AutoInfoVO;
import org.cyhz.domain.OutVO;
import org.cyhz.domain.TimeWindow;
import org.cyhz.standardize.dao.CarInfoDao;
import org.cyhz.util.Log;
import org.cyhz.util.TimeWindowUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import ch.lambdaj.Lambda;

@Service("brandSeriesStandardizeServiceRestfulImp")
public class BrandSeriesStandardizeServiceRestfulImp implements
		BrandSeriesStandardizeService {

	private ObjectMapper objectMapper = new ObjectMapper();

	@Autowired
	private RestTemplate restTemplate;

	@Value("${std_url}")
	private String std_url;

	@Autowired
	private CarInfoDao carInfoDao;

	@Value("#{configSettings['step2.time.window.width']}")
	private int timeWindowWidth;

	@Value("#{configSettings['other.brand']}")
	private String otherBrand;

	@Value("#{configSettings['other.series']}")
	private String otherSeries;

	@Value("#{configSettings['startdate']}")
	private String startDate;

	@Value("#{configSettings['enddate']}")
	private String endDate;

	public Date getStartDate() throws ParseException {
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
		return df.parse(this.startDate);
	}

	public Date getEndDate() throws ParseException {

		Date d = null;
		if (null == this.endDate || this.endDate.trim().equals("")) {
			d = new Date();
		} else {
			DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
			d = df.parse(this.endDate);
		}
		d = TimeWindowUtil.getLastSecondOfDay(d);
		return d;
	}

	@Override
	public void standardize() throws SQLException {

		Date startDate = null;
		Date endDate = null;
		try {
			startDate = this.getStartDate();
		} catch (ParseException e) {
			Log.error("get startDate error", e);
		}

		try {
			endDate = this.getEndDate();
		} catch (ParseException e) {
			Log.error("get endDate error", e);
		}

		List<TimeWindow> timeWindows = null;
		try {
			timeWindows = TimeWindowUtil.getContiguousTimeWindows(
					this.getStartDate(), this.getEndDate(), timeWindowWidth);
		} catch (ParseException e) {
			Log.error("build contiguous time window error", e);
		}

		if (null != timeWindows) {

			for (TimeWindow tw : timeWindows) {

				List<AutoInfoVO> ais = carInfoDao.getCarVoByNbNsAndReleaseTime(
						tw.getStartDate(), tw.getEndDate());

				for (AutoInfoVO ai : ais) {

					if (null == ai) {
						// dirty data
						continue;
					}

					Long now = new Date().getTime();
					Log.info("start..." + ai.toString());

					StandardizeResult sr = this.standardize(ai);

					int effectRow = carInfoDao.updateSbSsErrStepByNbNsTC(
							sr.getSb(), sr.getSs(), ai.getBrand(),
							ai.getSeries(), ai.getTitle(), ai.getAutoName(),
							startDate, endDate, sr.getStd_err_steps());

					Log.info(ai.toString() + "--->" + sr.getSb() + "|"
							+ sr.getSs() + " effect " + effectRow + " cost"
							+ (new Date().getTime() - now));

				}
				Log.info("finish step2 " + tw.toString());
			}
		}
	}

	private String urlUTF8Encode(String s) throws UnsupportedEncodingException {
		if (null == s) {
			return null;
		} else {
			return URLEncoder.encode(s, "UTF-8");
		}
	}

	public StandardizeResult standardize(AutoInfoVO ai) throws SQLException {
		StandardizeResult sr = null;
		String result = "";

		String t = ai.getTitle();
		String c = ai.getAutoName();
		String nb = ai.getBrand();
		String ns = ai.getSeries();

		try {
			t = urlUTF8Encode(t);
			c = urlUTF8Encode(c);
			nb = urlUTF8Encode(nb);
			ns = urlUTF8Encode(ns);
		} catch (UnsupportedEncodingException e1) {
			Log.error("url UTF8 encode error", e1);
		}

		String url = new StringBuilder(std_url).append("?t=").append(t)
				.append("&c=").append(c).append("&nb=").append(nb)
				.append("&ns=").append(ns).append("&encode=").append("UTF-8")
				.toString();

		Log.info("access url:" + url);

		try {
			result = restTemplate.getForObject(url, String.class);
		} catch (RestClientException e) {
			Log.error("std remote error", e);
		}

		try {
			sr = new StandardizeResult(otherBrand, otherSeries);
			OutVO vo = callback(result);
			sr.setSb(vo.getSb());
			sr.setSs(vo.getSs());
			sr.setStd_err_steps(vo.getStep());
		} catch (Exception e) {
			Log.error("parse sbstd remote result error", e);
		}
		return sr;
	}

	private OutVO callback(String data) {
		OutVO vo = new OutVO();
		vo.setSs(otherSeries);
		vo.setSb(otherBrand);

		try {
			@SuppressWarnings("unchecked")
			Map<String, Map<String, Object>> m = objectMapper.readValue(data,
					Map.class);
			Map<String, Object> map = m.get("result");

			String ss = String.valueOf(map.get("ss"));
			String sb = String.valueOf(map.get("sb"));

			if (StringUtils.isBlank(ss) || StringUtils.isBlank(sb)) {
				@SuppressWarnings("unchecked")
				String steps = Lambda.join(((List<String>) map.get("steps")));
				vo.setStep(steps);
			}

			if (!StringUtils.isBlank(ss)) {
				vo.setSs(ss);
			}
			if (!StringUtils.isBlank(sb)) {
				vo.setSb(sb);
			}

			return vo;
		} catch (IOException e) {
			Log.error("parse sbstd result error");
		}
		return null;
	}

}
